// LANDeskUtilDlg.cpp : implementation file
//

#include "stdafx.h"
#include "LANDeskUtil.h"
#include "LANDeskUtilDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CLANDeskUtilDlg dialog

#define LANDESKPATH	_T("C:\\Program Files\\LANDesk")
#define PATCHFILE	_T("patch.exe")

static const char LANDESKFILES[][256] = 
{
	"LDClient\\softmon.exe",
	"LDClient\\ProcTriggerSvc.exe",
	"LDClient\\tracksvc.exe",
	"LDClient\\amtmon.exe",
	"Shared Files\\residentagent.exe",
	"LDClient\\tmcsvc.exe",
	"LDClient\\issuser.exe",
	"LDClient\\policy.client.invoker.exe",
};

static const int NUM_LANDESKFILES = sizeof(LANDESKFILES) / sizeof(LANDESKFILES[0]);

CLANDeskUtilDlg::CLANDeskUtilDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CLANDeskUtilDlg::IDD, pParent)
	, m_strPath(LANDESKPATH)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CLANDeskUtilDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDT_PATH, m_edtPath);
	DDX_Control(pDX, IDOK, m_btnPatchIt);
	DDX_Control(pDX, IDC_LIST_ECHO, m_lstEchos);
}

BEGIN_MESSAGE_MAP(CLANDeskUtilDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDOK, &CLANDeskUtilDlg::OnBnClickedOk)
END_MESSAGE_MAP()


// CLANDeskUtilDlg message handlers

BOOL CLANDeskUtilDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	m_edtPath.SetWindowText(m_strPath);
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CLANDeskUtilDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CLANDeskUtilDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CLANDeskUtilDlg::Echo(const char * fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	CString strMessage;
	strMessage.FormatV(fmt, args);
	va_end(args);

	m_lstEchos.AddString(strMessage);
}

BOOL CLANDeskUtilDlg::PatchIt(const CString & strPatch, const CString & strTarget)
{
	BOOL bTargetExists = TRUE;
	BOOL bFailed = FALSE;
	DWORD dwAttrib = GetFileAttributes(strTarget);
	if(dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE)))
	{
		bTargetExists = FALSE;
	}

	if(!bTargetExists)
	{
		bFailed = (CopyFile(strPatch, strTarget, FALSE) == 0);
	}
	else
	{
		CString strTargetTemp;
		strTargetTemp.Format("%s.tmp", (LPCTSTR) strTarget);
		if(CopyFile(strPatch, strTargetTemp, FALSE) == 0)
		{
			bFailed = TRUE;
			goto done;
		}

		CString strTargetPatched;
		strTargetPatched.Format("%s.bak", (LPCTSTR) strTarget);
		if(MoveFileEx(strTarget, strTargetPatched, MOVEFILE_DELAY_UNTIL_REBOOT) == 0)
		{
			bFailed = TRUE;
			goto done;
		}

		if(MoveFileEx(strTargetTemp, strTarget, MOVEFILE_DELAY_UNTIL_REBOOT) == 0)
		{
			bFailed = TRUE;
			goto done;
		}
	}

done:
	if(!bFailed)
	{
		Echo("%s will be patched after reboot.", (LPCTSTR)strTarget);
	}
	else
	{
		Echo("Patch %s failed, error 0x%X", (LPCTSTR)strTarget, GetLastError());
	}
	return !bFailed;
}

BOOL CLANDeskUtilDlg::PatchIt()
{
	m_edtPath.GetWindowText(m_strPath);
	DWORD dwAttrib = GetFileAttributes(m_strPath);
	if(dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
	{
		Echo("%s is not a directory", (LPCTSTR)m_strPath);
		return FALSE;
	}
	
	char pszPatchPath[1024] = {0};
	GetModuleFileName(NULL, pszPatchPath, 1024);
	char * loc = strrchr(pszPatchPath, '\\');
	if(loc != NULL)
		loc[0] = '\0';

	CString strPatchFile;
	strPatchFile.Format("%s\\%s", pszPatchPath, PATCHFILE);
	dwAttrib = GetFileAttributes(strPatchFile);
	if(dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL)))
	{
		Echo("Patch file %s does not exist", (LPCTSTR) strPatchFile);
		return FALSE;
	}
	
	BOOL bPatched = FALSE;
	for(int idx = 0; idx < NUM_LANDESKFILES; idx ++)
	{
		CString strTargetFile;
		strTargetFile.Format("%s\\%s", (LPCTSTR) m_strPath, LANDESKFILES[idx]);
		if(PatchIt(strPatchFile, strTargetFile) && !bPatched)
		{
			bPatched = TRUE;
		}
	}
	
	if(bPatched)
	{
		Echo("Please reboot now!");
	}

	return bPatched;
}

void CLANDeskUtilDlg::OnBnClickedOk()
{
	if(PatchIt())
	{
		m_btnPatchIt.EnableWindow(FALSE);
	}
}
